// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: InstructionEmulator.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
/************************************************************************
**  
**  Copyright (C) 2002, Sun Microsystems, Inc.
**
**  Sun considers its source code as an unpublished, proprietary
**  trade secret and it is available only under strict license provisions.
**  This copyright notice is placed here only to protect Sun in the event 
**  the source is deemed a published work. Disassembly, decompilation,
**  or other means of reducing the object code to human readable form
**  is prohibited by the license agreement under which this code is
**  provided to the user or company in possession of this copy."
**
*************************************************************************/
#include "bcore/InstructionEmulator.h"
#include "Util/NumericUtils.h"
#include "Sf/Sf_Trap.h"
#include "Hv/Hv_Trap.h"
#include "StdMacros.h"
#include "MmuInterface.h"
#include <sstream>
#include <cassert>
#include <iomanip>

using namespace std;
using namespace Riesling;

/////////////////////////////////////////////////

InstructionEmulator::InstructionEmulator() :
    pstateAmMask_( ~0ULL ),
    reg_( "integer instr emulator" ),
    pstate_tct_enabled_(false)
{}

/////////////////////////////////////////////////

InstructionEmulator::InstructionEmulator( const std::string &name ) :
    pstateAmMask_( ~0ULL ),
    reg_( name ),
    pstate_tct_enabled_(false)
{}


/////////////////////////////////////////////////

InstructionEmulator::InstructionEmulator( const InstructionEmulator & orig )
{
    // Replace the following line with your function body.
    RIESLING_THROW_DOMAIN_ERROR( "Unimplemented function." );
}

/////////////////////////////////////////////////

InstructionEmulator::~InstructionEmulator()
{ }

/////////////////////////////////////////////////

const InstructionEmulator &
InstructionEmulator::operator=( const InstructionEmulator & rhs )
{
    // Replace the following line with your function body.
    RIESLING_THROW_DOMAIN_ERROR( "Unimplemented function." );

    return *this;
}

/////////////////////////////////////////////////

bool
InstructionEmulator::operator==( const InstructionEmulator & rhs ) const
{ 
    // Replace the following line with your function body.
    RIESLING_THROW_DOMAIN_ERROR( "Unimplemented function." );
    return false;
}

/////////////////////////////////////////////////

string
InstructionEmulator::toString() const
{
    ostringstream os;

    os << "\n"; 

    return os.str();
}

/////////////////////////////////////////////////

bool
InstructionEmulator::isPrivMode() const
{
     return pstate_->getPRIV() ? true : false;
}


bool InstructionEmulator::execute( InstructionWord &iw, V9_ArchState *asPtr )
{
    bool  result;

    if( !mmu_ ){
	RIESLING_THROW_DOMAIN_ERROR( "No mmu is accessable" );
    }

    if( !asiDispatcher_ ){
	RIESLING_THROW_DOMAIN_ERROR( "No asi dispatcher is accessable" );
    }

    if( !addressSpace_ ){
	RIESLING_THROW_DOMAIN_ERROR( "No address space is accessable" );
    }

    setArchState( asPtr );
    
    ctiExec_            = false;
    ctiTaken_           = false;
    
    if( !asPtr->getAnnulled() ){
// 	if( iw.nonZeroReservedBits() ){
// 	    RIESLING_TRAP( Sf_Trap::illegal_instruction );
// 	}
	if (iw.isPrivOpcodeTrap(isPrivMode(), isPrivMode(), tl_->getTL()))
	{
	    // priority 6 : privileged_opcode
	    RIESLING_TRAP( Sf_Trap::privileged_opcode );  
	}
	else if (iw.isIllegalInstrTrap(isPrivMode(), isPrivMode(), tl_->getTL(), asi_->getNative()))
	{
	    // priority 7.1 : illegal_instruction
	    RIESLING_TRAP( Sf_Trap::illegal_instruction );
	}

	result = exec_decode_me( iw );
    } else {
	iw.setAnnulled( true );
	asPtr->setAnnulled( false );
	result = true;
    }

    if ( result && !ctiExec_ ) {
        if (!pstate_tct_enabled_ || !ctiTaken_) {
    	    as_->setPc( as_->getNpc() );
	    as_->setNpc( as_->getNpc() + 4 );
        }
    }

    return result;
}


/////////////////////////////////////////////////

uint64_t InstructionEmulator::getPstateAmMask()
{
    return pstateAmMask_;
}

/////////////////////////////////////////////////

void InstructionEmulator::updatePstate( V9_PstateReg &pstate )
{

    if( pstate.getAM() ){
	pstateAmMask_ = 0x0ffffffffULL;
    } else {
	pstateAmMask_ = ~0x0ULL;
    }

    // NOTE: This is JPS1, not SPARC v9 here.
    uint8_t globalSelect = pstate.getAG() << 2 | pstate.getIG() << 1 | pstate.getMG();
    switch( globalSelect ){
	// normal
	case 0:
	    ireg_->selectGlobalSet( 0 );
	    break;
	case 1:
	    ireg_->selectGlobalSet( 1 );
	    break;
	case 2:
	    ireg_->selectGlobalSet( 2 );
	    break;
	case 4:
	    ireg_->selectGlobalSet( 3 );
	    break;
	default:
	    // TODO: add illegal_instruction trap here.
	    break;
    }
}

/////////////////////////////////////////////////

void InstructionEmulator::internalError( InstructionWord &iw )
{
    RIESLING_THROW_DOMAIN_ERROR( string("Internal Error on ").append( iw.toString() ) );
}

/////////////////////////////////////////////////

uint64_t 
InstructionEmulator::interruptLevelMask() const
{
    return 0x1f;
}

/////////////////////////////////////////////////

unsigned InstructionEmulator::getMaxTrapLevel() const
{
    // TODO do we want to set this?
    return 5;
}


/////////////////////////////////////////////////

bool InstructionEmulator::getCtiExecuted() const
{
    return ctiExec_;
}

/////////////////////////////////////////////////

bool InstructionEmulator::resetTrap( uint_t trapType ) const
{
    return 
        trapType == Sf_Trap::externally_initiated_reset ||
        trapType == Sf_Trap::power_on_reset || 
        trapType == Sf_Trap::watchdog_reset ||
	trapType == Sf_Trap::software_initiated_reset;
}

/////////////////////////////////////////////////

void InstructionEmulator::handleTrap( BasicTrap &trap )
{
    uint_t trapType = trap.getTrapType();
    uint_t tl = tl_->getTL();

    // TODO check trap number against max trap
    if( tl == getMaxTrapLevel() ){
	// error_state processing
    } 


    if( pstate_->getRED() ||
	tl == getMaxTrapLevel() - 1 ||
	( tl < getMaxTrapLevel() && 
	  trapType == Sf_Trap::software_initiated_reset ) ){

	saveTrapState( tl + 1, trapType );
	setRedTrapTablePc( tl, trapType );
	pstate_->setRED( 1 );
	tl_->setTL( tl + 1 );

    } else if( trapType == Sf_Trap::externally_initiated_reset ||
               trapType == Sf_Trap::watchdog_reset ){
	uint_t newTl = NumericUtils::min( tl + 1, getMaxTrapLevel() );
	saveTrapState( newTl, trapType );
	tl_->setTL( newTl );

    } else if( trapType == Sf_Trap::power_on_reset ){
	tl_->setTL( getMaxTrapLevel() );
	trapType_->setTrapType( getMaxTrapLevel(), trapType );
	tick_->setNPT( 1 );

    } else {
	saveTrapState( tl + 1, trapType );
	setTrapTablePc( tl, trapType );
	tl_->setTL( tl + 1 );
    }

    if( isMmuTrap( trap ) ){
	handleMmuTrap( trap, pstate_->getRED() );
    } else if( isIntrVectorTrap( trap ) ){
	handleIntrVectorTrap( trap, pstate_->getRED() );
    } else {
	handleNormalTrap( trap, pstate_->getRED() );
    }
}

/////////////////////////////////////////////////

bool InstructionEmulator::isMmuTrap( BasicTrap &trap )
{
    uint_t tt = trap.getTrapType();
    // fast_instruction_access_MMU_miss
    // fast_data_access_MMU_miss
    // fast_data_acess_protection
    return (0x064 <= tt && tt <= 0x06f) || 
	    tt == 0x008 ||  // instruction_access_exception
	    tt == 0x009 ||  // instruction_access_MMU_miss
	    tt == 0x030 ||  // data_access_exception
	    tt == 0x031 ||  // data_access_MMU_miss
	    tt == 0x033;    // data_access_proection
}

/////////////////////////////////////////////////

bool InstructionEmulator::isIntrVectorTrap( BasicTrap &trap )
{
    // interrupt_vector
    return trap.getTrapType() == 0x060;
}

/////////////////////////////////////////////////

void InstructionEmulator::handleNormalTrap( BasicTrap &trap, bool redState )
{
    if( !redState ){
	// Predefined normal-trap 
	pstate_->setRED( 0 );
	pstate_->setPEF( 1 );   // FPU is enabled
	pstate_->setAM( 0 );    // address masking turned off
	pstate_->setPRIV( 1 );  // processor enters privileged mode
	pstate_->setIE( 0 );    // interrupts are disabled
	pstate_->setAG( 1 );    // replace global regs with alternate globals
	pstate_->setMG( 0 );    // MMU globals are disabled
	pstate_->setIG( 0 );    // Interrupt globals are disabled
	pstate_->setCLE( pstate_->getTLE() );  // set endian mode for traps (TLE remains unchanged)
    }

    updatePstate( *pstate_ );

    uint_t trapType = trap.getTrapType();

    // Set CWP appropriately for register window traps
    if( trapType == 0x24 ){
	// clean_window trap
	ireg_->setCWP( ireg_->getCWP() + 1 );
    } else if( 0x80 <= trapType  && trapType <= 0xbf ){
	// window spill trap
	ireg_->setCWP( ireg_->getCWP() + ireg_->getCANSAVE() + 2 );
    } else if( 0xc0 <= trapType && trapType <= 0xff ){
	// window fill trap
	ireg_->setCWP( ireg_->getCWP() - 1 );
    }

}

///////////////////////////////////////////////////////

void InstructionEmulator::handleMmuTrap( BasicTrap &trap, bool redState )
{
    if( !redState ){
	// Predefined normal-trap 
	pstate_->setRED( 0 );
	pstate_->setPEF( 1 );   // FPU is enabled
	pstate_->setAM( 0 );    // address masking turned off
	pstate_->setPRIV( 1 );  // processor enters privileged mode
	pstate_->setIE( 0 );    // interrupts are disabled
	pstate_->setAG( 0 );    // replace global regs with alternate globals
	pstate_->setMG( 1 );    // MMU globals are disabled
	pstate_->setIG( 0 );    // Interrupt globals are disabled
	pstate_->setCLE( pstate_->getTLE() );  // set endian mode for traps (TLE remains unchanged)
    }

    updatePstate( *pstate_ );


}

///////////////////////////////////////////////////////

void InstructionEmulator::handleIntrVectorTrap( BasicTrap &trap, bool redState )
{

    if( !redState ){
	// Predefined normal-trap 
	pstate_->setRED( 0 );
	pstate_->setPEF( 1 );   // FPU is enabled
	pstate_->setAM( 0 );    // address masking turned off
	pstate_->setPRIV( 1 );  // processor enters privileged mode
	pstate_->setIE( 0 );    // interrupts are disabled
	pstate_->setAG( 0 );    // replace global regs with alternate globals
	pstate_->setMG( 0 );    // MMU globals are disabled
	pstate_->setIG( 1 );    // Interrupt globals are disabled
	pstate_->setCLE( pstate_->getTLE() );  // set endian mode for traps (TLE remains unchanged)
    }

    updatePstate( *pstate_ );

}

///////////////////////////////////////////////////////

void InstructionEmulator::handlePOR()
{
    pstate_->setMM  ( 0 ); // TSO
    pstate_->setRED ( 1 );
    pstate_->setPEF ( 1 ); // FPU is enabled
    pstate_->setAM  ( 0 ); // address masking turned off
    pstate_->setPRIV( 1 ); // processor enters privileged mode
    pstate_->setIE  ( 0 ); // interrupts are disabled
    pstate_->setAG  ( 1 ); // replace global regs with alternate globals
    pstate_->setMG  ( 0 ); // MMU globals are disabled
    pstate_->setIG  ( 0 ); // Interrupt globals are disabled
    pstate_->setTLE ( 0 ); // big-endian mode for traps
    pstate_->setCLE ( 0 ); // big-endian mode for non-traps

    updatePstate( *pstate_ );

}

///////////////////////////////////////////////////////

void InstructionEmulator::handleWDR()
{
    pstate_->setMM  ( 0 ); // TSO
    pstate_->setRED ( 1 );
    pstate_->setPEF ( 1 ); // FPU is enabled
    pstate_->setAM  ( 0 ); // address masking turned off
    pstate_->setPRIV( 1 ); // processor enters privileged mode
    pstate_->setIE  ( 0 ); // interrupts are disabled
    pstate_->setAG  ( 1 ); // replace global regs with alternate globals
    pstate_->setMG  ( 0 ); // MMU globals are disabled
    pstate_->setIG  ( 0 ); // Interrupt globals are disabled
    pstate_->setCLE( pstate_->getTLE() );  // set endian mode for traps (TLE remains unchanged)

    updatePstate( *pstate_ );

}

///////////////////////////////////////////////////////

void InstructionEmulator::handleXIR()
{
    pstate_->setMM  ( 0 ); // TSO
    pstate_->setRED ( 1 );
    pstate_->setPEF ( 1 ); // FPU is enabled
    pstate_->setAM  ( 0 ); // address masking turned off
    pstate_->setPRIV( 1 ); // processor enters privileged mode
    pstate_->setIE  ( 0 ); // interrupts are disabled
    pstate_->setAG  ( 1 ); // replace global regs with alternate globals
    pstate_->setMG  ( 0 ); // MMU globals are disabled
    pstate_->setIG  ( 0 ); // Interrupt globals are disabled
    pstate_->setCLE( pstate_->getTLE() );  // set endian mode for traps (TLE remains unchanged)

    updatePstate( *pstate_ );

}

///////////////////////////////////////////////////////

void InstructionEmulator::setTrapTablePc( uint_t tl, uint_t trapType )
{
    // Transfer control to the trap table.
    as_->setPc( tba_->getNative()  | ((tl_->getTL() > 0) << 14) | (trapType << 5) );
    as_->setNpc( as_->getPc() + 4 );
}

///////////////////////////////////////////////////////

void InstructionEmulator::setRedTrapTablePc( uint_t tl, uint_t trapType )
{
    if( resetTrap(trapType) ){
	as_->setPc( as_->getRSTVaddr() | ( trapType << 5 ) );
    } else {
	// V9 Manual, pg 91
	as_->setPc( as_->getRSTVaddr() + 0x0A0ULL );
    }
    as_->setNpc( as_->getPc() + 4 );
    // write_pc(red_mode_trap_base); 
    // RLOW(pc) |= (trap_num << TRAP_TYPE_SHIFT); 
    // write_npc(RADDW(pc, 4));
    // Transfer control to the trap table.
}
     
///////////////////////////////////////////////////////

void InstructionEmulator::saveTrapState( uint_t tl, uint_t trapType )
{
    V9_TstateEntry& e = tstate_->getTstateEntry( tl );

    e.setCcr( ccr_->getNative() );
    e.setAsi( asi_->getNative() );
    e.setPstate( pstate_->getNative() );
    e.setCwp( ireg_->getCWP() );

      tpc_->setTpc( tl, as_->getPc() & pstateAmMask_ );		// Sparc V9 OK (Hv overloads saveTrapState)
    tnpc_->setTnpc( tl, as_->getNpc() & pstateAmMask_ );	// Sparc V9 OK (Hv overloads saveTrapState)
    trapType_->setTrapType( tl, trapType );
}

///////////////////////////////////////////////////////

void InstructionEmulator::setAsiDispatcher( AsiDispatcher *d )
{
    asiDispatcher_ = d;
}

///////////////////////////////////////////////////////

void InstructionEmulator::setMmu( MmuInterface *mmu )
{
    mmu_ = mmu;
}

///////////////////////////////////////////////////////

void InstructionEmulator::setAddressSpace( AddressSpace *addressSpace )
{
    addressSpace_ = addressSpace;
}

///////////////////////////////////////////////////////

void InstructionEmulator::setArchState( V9_ArchState *asPtr )
{
    if( as_ != asPtr ){
	as_     = dynamic_cast<Sf_ArchState*>(asPtr);
	if( !as_  ){
	    RIESLING_THROW_INVALID_ARGUMENT( "Can't cast asPtr to Sf_ArchState*" );
	}
	ireg_   = as_->getRegisterFilePtr();
	freg_   = as_->getFloatRegisterFilePtr();
	pstate_ = as_->getPstateRegPtr();

	ccr_    = as_->getCcrRegPtr();
	y_      = as_->getYRegPtr();
	fprs_   = as_->getFprsRegPtr();
	fsr_    = as_->getFsrRegPtr();
	asi_    = as_->getAsiRegPtr();
	tpc_    = as_->getTpcRegPtr();
	tnpc_   = as_->getTnpcRegPtr();
	tick_   = as_->getTickRegPtr();
	stick_   = as_->getStickRegPtr();
	tickCompare_   = as_->getTickCmprRegPtr();
	stickCompare_   = as_->getStickCmprRegPtr();
	tba_    = as_->getTbaRegPtr();
	tl_     = as_->getTrapLevelRegPtr();
	tstate_ = as_->getTstateRegPtr();
	pil_      = as_->getPilRegPtr();
	wstate_ = as_->getWstateRegPtr();
	version_ = as_->getVerRegPtr();
	softint_ = as_->getSoftIntRegPtr();
	dcr_ = as_->getDcrRegPtr();
	pcr_ = as_->getPcrRegPtr();
	pic_ = as_->getPicRegPtr();
	trapType_ = as_->getTrapTypeRegPtr();
	gsr_ = as_->getGsrRegPtr();
    }
}

///////////////////////////////////////////////////////

V9_ArchState * InstructionEmulator::getArchState( ) const
{
    return as_;
}

///////////////////////////////////////////////////////

void InstructionEmulator::testSoftInt( ) const
{
    if( !pstate_->getIE() || !softint_->getNative() ){
	return;
    }

    uint_t newIntrLevel = 0;
    uint_t softIl = softint_->getINT_LEVEL() << 1;
    uint_t pil    = pil_->getTL();

    if( (15 > pil) && (softIl & (1 << 15)) ){
	newIntrLevel = 15;
    } else if( 14 > pil && 
              stickCompare_->getINT_DIS() == 0 &&
              stickCompare_->getCMPR() == stick_->getCOUNTER() ){
        newIntrLevel = 14;
    } else if( 14 > pil && 
              tickCompare_->getINT_DIS() == 0 &&
              tickCompare_->getCMPR() == tick_->getCOUNTER() ){
        newIntrLevel = 14;
    } else {
        uint_t il = 14;
	while( (il > pil) && ((1 << il) & softIl) == 0 ) --il;
	if( il > pil ) newIntrLevel = il;
    }

    if( newIntrLevel ){
	RIESLING_TRAP( 0x040 + newIntrLevel );
    }
}

///////////////////////////////////////////////////////

void InstructionEmulator::setParentRegistry( SimRegistry *parentPtr )
{
    reg_.setParent( parentPtr );

    Object *optr = NULL;

    // find an MMU
    if( (optr = reg_.findFirstParent( SimRegistry::MMU )) ){
	mmu_ = (MmuInterface*)( optr );
	if( !mmu_ ){
	    RIESLING_THROW_DOMAIN_ERROR( "Can't cast optr of type Object* to type Sf_Mmu*" );
	}
    } else {
	RIESLING_THROW_DOMAIN_ERROR( "Can't find any instances matching SimRegistry::MMU" );
    }

    // find an address space
    if( (optr = reg_.findFirstParent( SimRegistry::ADDRESS_SPACE )) ){
	addressSpace_ = dynamic_cast<AddressSpace*>( optr );
	if( !addressSpace_ ){
	    RIESLING_THROW_DOMAIN_ERROR( "Can't cast optr of type Object* to type AddressSpace*" );
	}
    } else {
	RIESLING_THROW_DOMAIN_ERROR( "Can't find any instances matching SimRegistry::ADDRESS_SPACE" );
    }

    // find an ASI dispatcher
    if( (optr = reg_.findFirstParent( SimRegistry::ASI_DISPATCHER )) ){
	asiDispatcher_ = dynamic_cast<AsiDispatcher*>( optr );
	if( !asiDispatcher_ ){
	    RIESLING_THROW_DOMAIN_ERROR( "Can't cast optr of type Object* to type AsiDispatcher*" );
	}
    } else {
	RIESLING_THROW_DOMAIN_ERROR( "Can't find any instances matching SimRegistry::ASI_DISPATCHER" );
    }



}

bool InstructionEmulator::exec_saved_restored (InstructionWord &i)
{
    uint32_t	rd = i.rd();

    if (rd == 0) {
	exec_SAVED(i);
    } else if (rd == 1) {
	exec_RESTORED(i);
    } 
    return true;
//     else {
// 	  RIESLING_TRAP( Sf_Trap::illegal_instruction );
//     }
}

void InstructionEmulator::unknown_instr (InstructionWord &i)
{
     RIESLING_TRAP( Sf_Trap::illegal_instruction );
}

#define SF_VA_HOLE_START        0x0000080000000000ULL
#define SF_VA_HOLE_END          0xFFFFF7FFFFFFFFFFULL

bool InstructionEmulator::inVaHole( uint64_t targetPc )
{
 
    return ((targetPc >= SF_VA_HOLE_START) && (targetPc <= SF_VA_HOLE_END)) ? true : false;

}

void InstructionEmulator::fpDirtyBitsWhenFpDisabled( bool value )
{
    fpDirtyBitsWhenFpDisabled_ = value;
}

bool InstructionEmulator::setFpDirtyBitsWhenFpDisabled()
{
    return fpDirtyBitsWhenFpDisabled_;
}
